package com.study.crypto.signer.pdf.digester;

import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfName;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.signatures.PdfSignatureAppearance;
import com.itextpdf.signatures.PdfSigner;
import com.study.crypto.signer.pdf.container.DigestBlankContainer;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

/**
 * pdf 文件摘要计算器
 * @author Songjin
 * @since 2022-05-06 13:43
 */
@Slf4j
public abstract class PdfDeferredDigester {
    
    private final Rectangle rectangle;
    private final int pageNumber;
    private byte[] toSignedBytes;
    
    protected PdfDeferredDigester(Rectangle rectangle, int pageNumber) {
        this.rectangle = rectangle;
        this.pageNumber = pageNumber;
    }
    
    /**
     * 计算 pdf 文件摘要
     * @param sigFieldName 签名域
     * @param certificate 签名人证书
     * @param input pdf文件流
     * @return 摘要
     * @throws GeneralSecurityException 异常
     * @throws IOException 异常
     */
    public byte[] digest(String sigFieldName, Certificate certificate, InputStream input) throws GeneralSecurityException, IOException {
        PdfName filter = this.getFilter();
        PdfName subFilter = this.getSubFilter();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        PdfReader reader = new PdfReader(input);
        PdfSigner signer = new PdfSigner(reader, baos, new StampingProperties());
        signer.setCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED);
        signer.setFieldName(sigFieldName);
        PdfSignatureAppearance appearance = signer.getSignatureAppearance();
        byte[] imageBytes = this.getImageBytes();
    
        String provider = BouncyCastleProvider.PROVIDER_NAME;
        CertificateFactory instance = CertificateFactory.getInstance("X.509", provider);
        ByteArrayInputStream certInput = new ByteArrayInputStream(certificate.getEncoded());
        X509Certificate x509Cert = (X509Certificate) instance.generateCertificate(certInput);
    
        appearance.setImage(ImageDataFactory.create(imageBytes))
                  .setCertificate(x509Cert)
                  .setPageRect(rectangle)
                  .setPageNumber(pageNumber);
    
        String digestAlgo = this.getDigestAlgorithm();
        DigestBlankContainer external = new DigestBlankContainer(filter, subFilter, digestAlgo);
        int estimatedSize = 8192;
        signer.signExternalContainer(external, estimatedSize);
    
        toSignedBytes = baos.toByteArray();
    
        reader.close();
        baos.flush();
        baos.close();
        return external.getDigest();
    }
    
    /**
     * 获取印章图片字节流
     * @return 图片字节流
     */
    public abstract byte[] getImageBytes();
    
    /**
     * 获取摘要算法名
     * @return 摘要算法名
     */
    public abstract String getDigestAlgorithm();
    
    /**
     * 获取 filter
     * @return PdfName
     */
    public abstract PdfName getFilter();
    
    /**
     * 获取 subfilter
     * @return PdfName
     */
    public abstract PdfName getSubFilter();
    
    public byte[] toSignedBytes() {
        return toSignedBytes;
    }
}
